﻿/*** MikeWare Framework ************************************
* This class is part of product of MikeWare.Framework.
* 
* Author      : Mike Cheers
* Mail        : mikecheers@126.com
* Create Date : 2018/9/14
* Summary     : 
* 
* 
* Modified By : 
* Date        : 
* Mail        : 
* Comment     : 
**************************************************************/

namespace MikeWare.Core.Configurations
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using MikeWare.Core.Extensions;
    using MikeWare.Core.Algorithms;
    using MikeWare.Core.Utilities;

    public class ConfigReader : IDisposable
    {
        private static ConfigReader instance = null;
        private static readonly object instanceLocker = new object();
        private static object threadSafeLocker = new object();
        private static Boolean disposed = false;
        private Dictionary<String, ConfigSection> ConfigSet = null;
        private const string DKEY = "MikeCheers.CoM";
        /// <summary>
        /// 是否以加密方式读取，默认值为False。
        /// </summary>
        private const Boolean DefaultEncrypted = false;
        /// <summary>
        /// 是否读取注释，默认值为False。
        /// </summary>
        private const Boolean DefaultReadComment = false;

        private ConfigReader()
        {
            disposed = false;
        }

        public static ConfigReader GetInstance()
        {
            if (null == instance || disposed)
                lock (instanceLocker)
                    if (null == instance || disposed)
                        instance = new ConfigReader();
            return instance;
        }

        public Dictionary<String, String> Read(String configFilePath)
        {
            return Read(configFilePath, DefaultEncrypted);
        }

        public Dictionary<String, String> Read(String configFilePath, Boolean encrypted)
        {
            return Read(configFilePath, encrypted, DefaultReadComment);
        }

        public Dictionary<String, String> Read(String configFilePath, Boolean encrypted, Boolean readcomment)
        {
            lock (threadSafeLocker)
            {
                if (String.IsNullOrWhiteSpace(configFilePath))
                    throw new ArgumentNullException("Invalide configFilePath");

                FileInfo file = new FileInfo(configFilePath);

                if (!file.Exists)
                    throw new ArgumentException("Config file not exists", configFilePath);
                if (null == ConfigSet)
                    ConfigSet = new Dictionary<string, ConfigSection>();
                String key = configFilePath.ToUpper().Trim();

                if (ConfigSet.ContainsKey(key))
                {
                    if (ConfigSet[key].Timestamp.Equals(file.LastWriteTime))
                        return ConfigSet[key].Items;
                    else
                        ConfigSet.Remove(key);
                }

                string content = null;

                if (encrypted)
                {
                    StringBuilder builder = new StringBuilder();

                    FileStream stream = file.OpenRead();
                    using (BinaryReader reader = new BinaryReader(stream))
                        while (stream.Position < stream.Length)
                        {
                            string line = EnAndDen.Decrypt(StreamHelper.ReadString(reader, Encoding.Unicode), DKEY);
                            builder.AppendLine(line);
                        }
                    content = builder.ToString().Trim();
                }
                else
                {
                    using (StreamReader reader = file.OpenText())
                        content = reader.ReadToEnd();
                }

                var section = new ConfigSection()
                {
                    Timestamp = file.LastWriteTime,
                    Items = AnlizeSectionItems(content, readcomment)
                };

                ConfigSet.Add(key, section);

                return ConfigSet[key].Items;
            }
        }

        private static Dictionary<string, string> AnlizeSectionItems(string content, bool readcomment)
        {
            string regexpat = @".*?\[[^\[\]]*(((?'group'\[)[^\[\]]*)+((?'-group'\])[^\[\]]*)+)*(?(group)(?!))\]";
            // @".*?\[[^{]*({[^}],?)+[^}]*}[^\]]*]"

            Dictionary<string, string> result = new Dictionary<string, string>();

            if (string.IsNullOrWhiteSpace(content)) return result;

            MatchCollection matches = Regex.Matches(content
                , regexpat
                , RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);

            foreach (Match match in matches)
            {
                AnlizeSectionLine(ref result, match.Value, readcomment);
            }

            content = Regex.Replace(content
                 , regexpat
                 , string.Empty
                 , RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);

            string[] arrayLines = content.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            foreach (string line in arrayLines)
            {
                //Trace.WriteLine("Is going to anlize line: " + line);
                AnlizeSectionLine(ref result, line, readcomment);
            }

            return result;
        }

        public static Dictionary<int, Dictionary<string, string>> AnlizeSectionArray(string content, bool readcomment)
        {
            string regexpat = @"(?<=\{)[^\{\}]*(((?'group'\{)[^\{\}]*)+((?'-group'\})[^\{\}]*)+)*(?(group)(?!))(?=\})";

            Dictionary<int, Dictionary<string, string>> result = new Dictionary<int, Dictionary<string, string>>();

            if (string.IsNullOrWhiteSpace(content)) return result;

            MatchCollection matches = Regex.Matches(content
                , regexpat
                , RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);

            foreach (Match match in matches)
            {
                Dictionary<string, string> subdic = AnlizeSectionItems(match.Value, readcomment);
                result.Add(result.Count, subdic);
            }

            return result;
        }

        private static void AnlizeSectionLine(ref Dictionary<string, string> section, string line, Boolean readcomment)
        {
            if (String.IsNullOrWhiteSpace(line)) return;
            if (line.StartsWith("#"))
                if (readcomment)
                    line = String.Format("#Comment_{0}={1}", section.Count, line);
                else
                    return;

            var array = line.Split("=".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
            if (array.Length != 2) return;

            var key = array[0].ToUpper().Trim();
            if (section.ContainsKey(key))
            {
                throw new Exception("配置节" + array[0] + ", " + section[key] + " 与 " + array[1].Trim() + "重复");
            }

            if (!String.IsNullOrWhiteSpace(array[1]))
                section.Add(key, array[1].Trim());
        }

        public static ConfigGroups AnlizeConfigGroups(string configvalue)
        {
            ConfigGroups groups = new ConfigGroups();

            //Regex regex = new Regex(@"<div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>");
            Regex regex = new Regex(@"([^\s,\[\]]+),??"
                , RegexOptions.Multiline | RegexOptions.IgnoreCase);

            MatchCollection matchs = regex.Matches(configvalue);

            if (null != matchs
                && matchs.Count > 0)
            {
                foreach (Match match in matchs)
                {
                    if (match.Success)
                        groups.Add(match.Value);
                }
            }
            else
            {
                groups.Add(configvalue);
            }

            return groups;
        }

        public void Dispose()
        {
            disposed = true;
            ConfigSet = null;
        }
    }
}
